/* 
 * Copyright 2012 Mikaël GUILLEMOT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.g2de;

import java.applet.Applet;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;

import org.g2de.display.Engine2D;
import org.g2de.display.MouseControlListener;
import org.g2de.gui.AbstractUI;

/**
 * classe de moteur general on gere ici la config, l'affichage, les
 * evenements,... LATER le fullscreen
 * 
 * @author Mikael GUILLEMOT
 * @version 1.0
 * @since
 * 
 */
public class BasicGameFrame implements MouseListener, MouseMotionListener, KeyListener {

	public final static int UNDEFINE_LAYER = -1;
	public final static int BACK_LAYER = 0;
	public final static int MAP_BASE_LAYER = 1;
	public final static int MAP_DECORATION_LAYER = 2;
	public final static int UNIT_LAYER1 = 3;
	public final static int UNIT_LAYER2 = 4;
	public final static int SKY_LAYER = 5;
	public final static int FOG_LAYER = 6;
	public final static int UI_SECOND_LAYER = 7;
	public final static int UI_MAIN_LAYER = 8;
	public final static int UI_TOP_LAYER = 9;

	public final static int MAX_LAYER_INDEX = 9;

	public final static int X_SIZE = 800;
	public final static int Y_SIZE = 600;

	private static Applet applet = null;
	private static BasicGameFrame e = null;

	private Frame frame = null;
	private Engine2D engine = null;
	private volatile List<MouseControlListener>[] mouseListenerLayers;

	// private Tools t = null;
	// private boolean FullScreen = true; TODO

	/**
	 * ajoute l'ui en parametre au systeme (evenement et affichage) TODO a
	 * etendre et, ou externaliser (pour gerer plus que les ui
	 */
	public void addUI(AbstractUI ui, int layer) {
		engine.register(ui, layer);
		mouseListenerLayers[layer].add(ui);

	}

	/**
	 * Supprime l'objet du mecanisme de transmission des evenements pour
	 * supprimer la limite utiliser Engine2D.NO_FPS_LIMIT
	 * 
	 * @param object
	 * @param layerIndex
	 */
	public void removeUI(AbstractUI ui, int layer) {
		engine.remove(ui, layer);
		mouseListenerLayers[layer].remove(ui);
	}

	/**
	 * initialise la limitation de fps (aucune par defaut)
	 * 
	 * @param limit
	 *            nombre de fps a ne pas depasser
	 */
	public void setFPSLimit(int limit) {
		engine.setFPSLimit(limit);
	}

	/**
	 * donne un nom a la fenetre (n'est affiché que dans la barre des taches)
	 * 
	 * @param title
	 *            titre
	 */
	public void setTitle(String title) {
		if (applet == null)
			frame.setTitle(title);
		else
			applet.setName(title);
	}

	/**
	 * Constructeur<br>
	 * Le SuppressWarnings correspond a l'impossibilité de faire new
	 * List<Renderable>[number]
	 */
	@SuppressWarnings("unchecked")
	private BasicGameFrame() {
		// TODO diferencier l'init sous forme d'applet
		// on prepare la liste des objets d'ui
		mouseListenerLayers = new List[MAX_LAYER_INDEX + 1];
		for (int i = 0; i < mouseListenerLayers.length; i++)
			mouseListenerLayers[i] = new ArrayList<MouseControlListener>(10);
		// on prepare la fenetre qui va aceuillir la gui
		Engine2D.X_SIZE = X_SIZE;
		Engine2D.Y_SIZE = Y_SIZE;
		engine = new Engine2D();
		if (applet == null) {
			frame = new JFrame();
			frame.setIgnoreRepaint(true);
			frame.setResizable(false);
			frame.setUndecorated(true);

			// on met le tout ensemble et on affiche
			frame.add(engine.getCanvas());
			frame.pack();
			Dimension scrSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
			frame.setLocation((scrSize.width - X_SIZE) / 2, (scrSize.height - Y_SIZE) / 2);
			frame.setVisible(true);
		} else {
			applet.setSize(X_SIZE, Y_SIZE + 16);
			applet.add(engine.getCanvas());
			applet.setIgnoreRepaint(true);
		}

		engine.setRenderingMode(Engine2D.HARDWARE_TRIPLE_BUFFER);
		engine.initLayers(MAX_LAYER_INDEX + 1);
		engine.setResolution(X_SIZE, Y_SIZE);
		// engine.setFPSLimit(Engine2D.NO_FPS_LIMIT);
		engine.drawFPS(true);
		engine.useAntiAliasing(false);
		engine.getCanvas().addMouseListener(this);
		engine.getCanvas().addMouseMotionListener(this);
		engine.getCanvas().addKeyListener(this);
	}

	/**
	 * retourne l'instance de engine, la cree au besoin
	 * 
	 * @return
	 */
	public static BasicGameFrame getInstance() {
		if (e == null)
			e = new BasicGameFrame();
		return e;
	}

	/**
	 * demare le moteur
	 */
	public void start() {

		if (engine.validate())
			engine.start();
	}

	private final void propagateMouseAction(MouseEvent arg0, short actionType) {
		for (List<MouseControlListener> renderlist : mouseListenerLayers)
			for (MouseControlListener r : renderlist) {
				r.mouseChange(arg0, actionType);
				if (arg0.isConsumed())// TODO a verifier comment on le consume
					return;
			}
	}

	/*
	 * private final void propagateKeyboardAction(KeyEvent arg0 ,short
	 * actionType) { //TODO propager l'action }
	 */

	/**
	 * renvoi la position de la fenetre par rapport a l'ecran
	 */
	public Point getWindowLocation() {
		// FIXME comportement pour l'applet
		if (applet == null)
			return frame.getLocationOnScreen();
		else
			return applet.getLocationOnScreen();
	}

	/**
	 * deplace la fenetre sur l'ecran (en x,y absolue)
	 * 
	 * @param x
	 * @param y
	 */
	public void moveWindow(int x, int y) {
		if (applet == null)
			frame.setLocation(x, y);
	}

	// gere la reduction de la fenetre (voir le alt+tab)
	public void minimize() {
		if (applet == null)
			frame.setState(Frame.ICONIFIED);
		// TODO prendre en compte l'arret du refresh

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent
	 * )
	 */
	@Override
	public void mouseDragged(MouseEvent arg0) {
		propagateMouseAction(arg0, MouseControlListener.EV_DRAG);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
	 */
	@Override
	public void mouseMoved(MouseEvent arg0) {
		propagateMouseAction(arg0, MouseControlListener.EV_MOVE);
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {
		propagateMouseAction(arg0, MouseControlListener.EV_CLICK);

	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO reprise de l'update (auto?)
		// ou gestion du defilement

	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO suspention de l'update (menu?)
		// ou gestion du defilement

	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		propagateMouseAction(arg0, MouseControlListener.EV_PRESSED);
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		propagateMouseAction(arg0, MouseControlListener.EV_RELEASED);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
	 */
	@Override
	public void keyPressed(KeyEvent e) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
	 */
	@Override
	public void keyReleased(KeyEvent e) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
	 */
	@Override
	public void keyTyped(KeyEvent e) {
		// propagateKeyboardAction()
		// TODO Auto-generated method stub

	}

	/**
	 * A appeler avant toute chose : si on utilise une appelet
	 */
	public static void runAsApplet(Applet ap) {
		applet = ap;
	}

	/**
	 * ordone l'arret de l'affichage (et attend la fin le l'image en cours)
	 */
	public void stop() {
		if (engine != null)
			try {
				engine.exit();
				engine.join();
				engine = null;
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		if (applet == null && frame != null)
			frame.dispose();
		else
			applet = null;
		e = null;
	}
}
